#!/usr/bin/env node
"use strict";
const { execSync } = require('child_process');
const { rmSync, mkdirSync, existsSync, readFileSync, writeFileSync, readdirSync } = require('fs');
const { join, parse } = require('path',);

let protoPath = process.argv[2];
if (typeof protoPath === 'undefined') {
    console.error('Need to specify the proto file directory path');
    process.exit(1);
}

let pbJsOutPath = process.argv[3];
if (typeof pbJsOutPath === 'undefined') {
    console.error('Need to specify the pb javascript file out directory path');
    process.exit(1);
}

let pbTsOutPath = process.argv[4];
if (typeof pbTsOutPath === 'undefined') {
    console.error('Need to specify the pb typesript file out directory path');
    process.exit(1);
}


function toCamelCase(filename) {
    return filename
        .replace(/\.proto$/, '') // 去除扩展名
        .split('_') // 分割下划线
        .map((part, index) => 
            index === 0 ? part.toLowerCase() : 
            part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()
        )
        .join('') + 'Pb'; // 拼接小驼峰并添加Pb后缀
}

function handleOutPath(path) {
    // rmSync(pbJsOutPath, { recursive: true, force: true });
    if (!existsSync(path)) {
        mkdirSync(path, { recursive: true });
    }
    else {
        rmSync(path, { recursive: true, force: true })
    }
}

function runCmd(cmd) {
    execSync(cmd);
}

function esModuleCorrect(path) {
    let file = readFileSync(path, { encoding: 'utf8' });
    // let result = file.replace("import * as $protobuf", "import { default as $protobuf }");
    // es导入最终存储在commonjs的内容是另一份对象
    let result = file.replace(`const $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});`, "const $root = {};");
    result = result.replace(/\$protobuf\./g, "$protobuf.default.");
    let extraContent = [`import Long from 'long';`, `$protobuf.default.util.Long = Long;`, `$protobuf.default.configure();`];
    let dataArray = result.split(/\r\n|\n|\r/gm);
    dataArray.splice(2, 0, ...extraContent);
    writeFileSync(path, dataArray.join('\r\n'));
}

function genEsModuleConfig(path) {
    let config = {"type": "module"};
    writeFileSync(path, JSON.stringify(config, null, 4));
}

handleOutPath(pbJsOutPath);
handleOutPath(pbTsOutPath);

// 新增遍历逻辑
function processProtos() {
    const protoFiles = readdirSync(protoPath)
        .filter(file => file.endsWith('.proto'));

    handleOutPath(pbJsOutPath);
    handleOutPath(pbTsOutPath);    
    const cmdMappings = [];
    const imports = [];
    const msgNameMappings = [];
    const cmdEnums = [];

    protoFiles.forEach(file => {
        const { name: protoName } = parse(file);
        const pbBaseName = toCamelCase(protoName);
        
        const outJsFilepath = join(pbJsOutPath, `${pbBaseName}.js`);
        const outTsFilepath = join(pbTsOutPath, `${pbBaseName}.d.ts`);
        const protoFullPath = join(protoPath, file);

        // 生成命令
        // pbjs -t static-module -w commonjs -o bundle.js proto/*.proto
        // const pbjsCmd = `pbjs -t static-module -w commonjs -o ${outJsFilepath} ${protoFullPath}`
        const pbjsCmd = `pbjs --dependency protobufjs/minimal.js --target static-module --wrap es6 --force-long --force-message --no-verify --no-delimited --no-beautify --no-service --out ${outJsFilepath} ${protoFullPath}`;
        const pbtsCmd = `pbts --main --no-comments --name ${pbBaseName} --out ${outTsFilepath} ${outJsFilepath}`;

        // const pbtsCmd = `pbts -o ${outTsFilepath} ${outJsFilepath}`
        
        // 保持原有构建流程
        console.time(`build ${protoName}`);

        console.log(`文件是：${file}`);
        runCmd(pbjsCmd);
        runCmd(pbtsCmd);
        rmSync(outJsFilepath);
        runCmd(pbjsCmd.concat(" --no-comments"));
        esModuleCorrect(outJsFilepath);
        console.timeEnd(`build ${protoName}`);

        const content = readFileSync(join(protoPath, file), 'utf-8');
        const regex = /\/\/\s*CMD_MAIN_ID:(\d+)\s*CMD_SUB_ID:(\d+)[\s\S]*?message\s+(\w+)\s*{/gs;
        const cs_regex = /\s*(c2s|s2c)/;
        let match;
        let is_match = false;
        while ((match = regex.exec(content)) !== null) {
            let cmdMainId = match[1];
            let cmdSubId = match[2];
            let messageName = match[3];
            let matchCsType = cs_regex.exec(messageName);
            let csType = matchCsType[1];
            is_match = true;
            // console.log(`消息: ${cmdMainId}_${cmdSubId} ${messageName}  ${csType}`);
            cmdMappings.push(`"${cmdMainId}_${cmdSubId}_${csType}": ${pbBaseName}.Game.${messageName}`);
            msgNameMappings.push(`[${pbBaseName}.Game.${messageName}.name]: [${cmdMainId}, ${cmdSubId}, "${csType}"]`);
            cmdEnums.push(`${messageName} = "${messageName}"`);
          }
        if (is_match) {
            imports.push(`import ${pbBaseName} from './${pbBaseName}';`);
        }

    });

    // 只生成一次ES模块配置
    genEsModuleConfig(join(pbJsOutPath, "package.json"));

    // 生成消息映射
    gen_message_mappings(join(pbJsOutPath, "protoMappings.ts"), cmdMappings, msgNameMappings, imports, cmdEnums);
}

function gen_message_mappings(outputFile, cmdMappings, msgNameMappings, imports, cmdEnums) {
    const importsCode = `${imports.join('\n')}\n`;
    const byCmdCode = `export const MessageMapByCmd = {\n  ${cmdMappings.join(',\n  ')}\n};\n`;
    const byMsgCode = `export const MessageMapByMsgName = {\n  ${msgNameMappings.join(',\n  ')}\n};\n`;
    const msgEnumsCode = `export const enum PbCmd {\n  ${cmdEnums.join(',\n  ')}\n};\n`;
    writeFileSync(outputFile, importsCode + byCmdCode + byMsgCode + msgEnumsCode);
    
}


processProtos();